/**
 * 
 */
package truerefactor.metrics;

import java.util.List;

import truerefactor.graph.AttributeNode;
import truerefactor.graph.ClassNode;
import truerefactor.graph.CodeGraph;
import truerefactor.graph.CodeNode;
import truerefactor.graph.MethodNode;

/**
 * LCOM - An implementation of the Lack of Cohesion in Object Methods metric
 * 
 * @author Isaac Griffith
 */
public class LCOM extends Metric {

    /**
     * Creates a new LCOM Metric instance
     */
    public LCOM()
    {
        this.shortName = "LCOM";
        this.name = "Lack of Cohesion between Methods";
        this.description = "";
    }

    /*
     * (non-Javadoc)
     * @see truerefactor.metrics.Metric#measure()
     */
    @Override
    public double measure(CodeGraph graph)
    {
        // The number of methods that access one or more of the same attributes
        // Low LCOM = high cohesion = Good
        // measure by looking at the connections within a class

        double tLCOM = 0; // the total LCOM of the hierarchy
        double mLCOM = 0; // the mean LCOM

        List<ClassNode> classes = graph.getClasses();
        for (ClassNode cNode : classes)
        {
            tLCOM += measure(cNode);
        }

        mLCOM = (double) (tLCOM / classes.size());

        return mLCOM;
    }

    /**
     * Measures the average LCOM for the provided ClassNode
     * 
     * @param cNode
     *            ClassNode to be measured
     * @return the provided class nodes average LCOM
     */
    public static double measure(ClassNode cNode)
    {
        double cLCOM = 0;
        int access = 0;
        int noaccess = 0;
        List<CodeNode> methods = cNode.getMethods();
        List<CodeNode> attributes = cNode.getAttributes();

        for (CodeNode attribute : attributes)
        {
            AttributeNode attr = (AttributeNode) attribute;

            int count = 0;
            int nocount = 0;
            for (CodeNode meth : methods)
            {
                MethodNode method = (MethodNode) meth;
                if (method.getAssocAttrs().contains(attr))
                {
                    count++;
                }
                else
                {
                    nocount++;
                }
            }

            access += count / 2;
            noaccess += nocount / 2;
        }

        cLCOM = noaccess - access;

        if (cLCOM < 0)
            return cLCOM = 0;

        return cLCOM;
    }
}
